package org.jing.core.lang.data;

import org.jing.core.lang.ExceptionHandler;
import org.jing.core.lang.JingException;
import org.jing.core.util.FileUtil;
import org.jing.core.util.StringUtil;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;

/**
 * Description: 文件内容. <br>
 *
 * @author bks <br>
 * @since 2024-07-22 <br>
 */
@SuppressWarnings({ "WeakerAccess", "unused" })
public final class FileContent implements Closeable {
    private final String lineSeparator;

    private boolean closed;

    private final List<String> contents;

    /**
     * Description: 创建一个实例. <br>
     */
    public FileContent() {
        contents = new ArrayList<>();
        closed = false;
        lineSeparator = java.security.AccessController.doPrivileged(new sun.security.action.GetPropertyAction("line.separator"));
    }

    public String getLineSeparator() {
        return lineSeparator;
    }

    /**
     * Description: 增加一行文本. <br>
     *
     * @param row 文本 <br>
     */
    public void add(String row) {
        checkEnable();
        contents.add(row);
    }

    /**
     * Description: 设置一行文本. <br>
     *
     * @param index 索引 <br>
     * @param row 文本 <br>
     */
    public void set(int index, String row) {
        checkEnable();
        contents.set(index, row);
    }

    /**
     * Description: 设置一行文本. <br>
     *
     * @param index 索引 <br>
     */
    public void remove(int index) {
        checkEnable();
        contents.remove(index);
    }

    /**
     * Description: 增加多行文本. <br>
     *
     * @param rows 多行文本 <br>
     */
    public void addAll(List<String> rows) {
        checkEnable();
        contents.addAll(rows);
    }

    /**
     * Description: 获取行数. <br>
     *
     * @return <br>
     */
    public int size() {
        return contents.size();
    }

    /**
     * Description: 获取指定索引行. <br>
     *
     * @param index 行索引 <br>
     * @return <br>
     */
    public String get(int index) {
        return contents.get(index);
    }

    /**
     * Description: 遍历. <br>
     *
     * @param action 方法 <br>
     */
    public void forEach(Consumer<String> action) {
        Objects.requireNonNull(action);
        contents.forEach(action);
    }

    /**
     * Description: 使用'\n‘作为换行符输出成字符串. <br>
     *
     * @return <br>
     */
    public String asString() {
        return asString("\n");
    }

    /**
     * Description: 使用指定换行符输出成字符串. <br>
     *
     * @param lineSeparator 换行符 <br>
     * @return 字符串 <br>
     */
    public String asString(String lineSeparator) {
        Wrapper<String> separator = new Wrapper<>(StringUtil.ifEmpty(lineSeparator, FileUtil.SYSTEM_LINE_SEPARATOR));
        StringBuilder stbr = new StringBuilder();
        contents.forEach(r$ -> {
            if (stbr.length() != 0) stbr.append(separator.get());
            stbr.append(r$);
        });
        return stbr.toString();
    }

    /**
     * Description: 将文件使用指定编码读取为字符串. <br>
     * 如果指定编码为空, 使用<b>utf-8</b>作为读取编码 <br>
     *
     * @param file 指定文件 <br>
     * @param encoding 指定编码 <br>
     * @return <br>
     */
    public static FileContent readFile(File file, String encoding) {
        ExceptionHandler.publishIfMatch(!FileUtil.checkFileExist(file), "file not readable: [{}]", null == file ? null : file.getAbsolutePath());
        try (
            BufferedReader br = FileUtil.getBufferedReader(file, StringUtil.ifEmpty(encoding, "utf-8"));
            FileContent content = new FileContent()
        ) {
            String row;
            while (null != (row = br.readLine())) {
                content.add(row);
            }
            return content;
        }
        catch (Throwable t) {
            throw new JingException(t, "failed To readFile file [{}]", file.getAbsolutePath());
        }
    }

    /**
     * Description: 将指定路径的文件使用指定编码读取为字符串. <br>
     * 如果指定编码为空, 使用<b>utf-8</b>作为读取编码 <br>
     *
     * @param filePath 指定文件路径 <br>
     * @param encoding 指定编码 <br>
     * @return <br>
     */
    public static FileContent readFile(String filePath, String encoding) {
        return readFile(new File(filePath), encoding);
    }

    /**
     * Description: 将文件使用默认编码(<b>utf-8</b>)读取为字符串. <br>
     *
     * @param file 指定文件 <br>
     * @return <br>
     */
    public static FileContent readFile(File file) {
        return readFile(file, null);
    }

    /**
     * Description: 将指定路径的文件使用默认编码(<b>utf-8</b>)读取为字符串. <br>
     *
     * @param filePath 指定文件路径 <br>
     * @return <br>
     */
    public static FileContent readFile(String filePath) {
        return readFile(new File(filePath), null);
    }

    /**
     * Description: 将资源文件使用指定编码读取为字符串. <br>
     * 一般用于和classpath相关的场景. <br>
     * 如果指定编码为空, 使用<b>utf-8</b>作为读取编码. <br>
     *
     * @param resourcePath 资源路径 <br>
     * @param encoding 指定编码 <br>
     * @return <br>
     */
    public static FileContent readResource(String resourcePath, String encoding) {
        try (
            BufferedReader br = new BufferedReader(new InputStreamReader(FileUtil.getResourceInputStream(resourcePath),
                StringUtil.ifEmpty(encoding, "utf-8")));
            FileContent content = new FileContent()
        ) {
            String row;
            while (null != (row = br.readLine())) {
                content.add(row);
            }
            return content;
        }
        catch (Throwable t) {
            throw new JingException(t, "failed To read resource [{}]", resourcePath);
        }
    }

    /**
     * Description: 将资源文件使用默认编码(<b>utf-8</b>)读取为字符串. <br>
     * 一般用于和classpath相关的场景. <br>
     * 如果指定编码为空, 使用<b>utf-8</b>作为读取编码. <br>
     *
     * @param resourcePath 资源路径 <br>
     * @return <br>
     */
    public static FileContent readResource(String resourcePath) {
        return readResource(resourcePath, "utf-8");
    }

    /**
     * Description: 将文本写入文件. <br>
     * 编码不设置或为空时使用默认编码<b>utf-8</b>. <br>
     *
     * @param file 文件 <br>
     * @param append 追加模式 <br>
     * @param encoding 编码 <br>
     */
    public void write(File file, boolean append, String encoding) {
        File parent = file.getParentFile();
        if (!FileUtil.mkdirs(parent)) {
            throw new JingException("failed to create parent path");
        }
        try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, append), StringUtil.ifEmpty(encoding, "utf-8")))) {
            for (int i$ = 0, l$ = size(); i$ < l$; i$ ++) {
                if(0 != i$) {
                    writer.write(lineSeparator);
                }
                writer.write(get(i$));
            }
            writer.flush();
        }
        catch (Throwable t) {
            throw new JingException(t, "failed to write file [{}]", file.getAbsolutePath());
        }
    }

    /**
     * Description: 将文本写入文件. <br>
     * 不追加, 直接覆盖. <br>
     *
     * @param file 文件 <br>
     * @param encoding 编码 <br>
     */
    public void write(File file, String encoding) {
        write(file, false, encoding);
    }

    /**
     * Description: 将文本写入文件. <br>
     * 使用默认<b>utf-8</b>编码. <br>
     *
     * @param file 文件 <br>
     * @param append 追加模式 <br>
     */
    public void write(File file, boolean append) {
        write(file, append, null);
    }

    /**
     * Description: 将文本写入文件. <br>
     * 不追加, 直接覆盖. <br>
     * 使用默认<b>utf-8</b>编码. <br>
     *
     * @param file 文件 <br>
     */
    public void write(File file) {
        write(file, false, null);
    }

    /**
     * Description: 将文本写入指定路径的文件. <br>
     * 编码不设置或为空时使用默认编码<b>utf-8</b>. <br>
     *
     * @param filePath 文件路径 <br>
     * @param append 追加模式 <br>
     * @param encoding 编码 <br>
     */
    public void write(String filePath, boolean append, String encoding) {
        write(new File(filePath), append, encoding);
    }

    /**
     * Description: 将文本写入指定路径的文件. <br>
     * 不追加, 直接覆盖. <br>
     *
     * @param filePath 文件路径 <br>
     * @param encoding 编码 <br>
     */
    public void write(String filePath, String encoding) {
        write(new File(filePath), false, encoding);
    }

    /**
     * Description: 将文本写入指定路径的文件. <br>
     * 使用默认<b>utf-8</b>编码. <br>
     *
     * @param filePath 文件路径 <br>
     * @param append 追加模式 <br>
     */
    public void write(String filePath, boolean append) {
        write(new File(filePath), append, null);
    }

    /**
     * Description: 将文本写入指定路径的文件. <br>
     * 不追加, 直接覆盖. <br>
     * 使用默认<b>utf-8</b>编码. <br>
     *
     * @param filePath 文件路径 <br>
     */
    public void write(String filePath) {
        write(new File(filePath), false, null);
    }

    private void checkEnable() {
        if (closed) {
            throw new JingException("file content has closed");
        }
    }

    @Override
    public void close() {
        closed = true;
    }
}
